#include "shader.h"
#include "file.h"
#include "error.h"

shader::shader(char* vtxname,char* pixname):
vtx_shader(0),pix_shader(0),program(0){
    error::log(error::con_only,"Chargement du shader %s | %s...",vtxname,pixname);
    vtx_shader = glCreateShader(GL_VERTEX_SHADER);
    pix_shader = glCreateShader(GL_FRAGMENT_SHADER);
    program = glCreateProgram();
    if( !valid() ){
        error::log(error::con_only,"Erreur\n");
        error::log(error::log_warn,"Shader %s | %s: Impossible de creer les identifiants, annulation...",vtxname,pixname);
        glDeleteShader(pix_shader);
        glDeleteShader(vtx_shader);
        glDeleteProgram(program);
        vtx_shader = pix_shader = program = 0;
        return;
    }

    file srcfile;
    if( !srcfile.open(vtxname) ){
        error::log(error::con_only,"Erreur\n");
        error::log(error::log_warn,"Vertex shader %s: fichier introuvable, annulation...",vtxname);
        glDeleteShader(pix_shader);
        glDeleteShader(vtx_shader);
        glDeleteProgram(program);
        vtx_shader = pix_shader = program = 0;
        return;
    }
    srcfile.seek(0,SEEK_END);
    long int filesize = srcfile.tell();
    srcfile.rewind();
    char* source = new char[filesize + 1];
    srcfile.read(source,1,filesize);
    source[filesize] = '\0';
    glShaderSource( vtx_shader, 1, (const char**)&source, NULL);
    glCompileShader( vtx_shader );
    GLint compile_status = GL_TRUE;
    glGetShaderiv( vtx_shader, GL_COMPILE_STATUS, &compile_status );
    if( compile_status != GL_TRUE ){
        error::log(error::con_only,"Erreur\n");
        error::log(error::log_warn,"Vertex shader %s: erreur lors de la compilation, annulation...",vtxname);
        GLint logsize;
        glGetShaderiv(vtx_shader,GL_INFO_LOG_LENGTH,&logsize);
        char* log = new char[ logsize+1 ];
        glGetShaderInfoLog(vtx_shader,logsize,&logsize,log);
        error::log(error::log_warn,"%s->%s",source,log);
        delete log;
        delete source;
        glDeleteShader(pix_shader);
        glDeleteShader(vtx_shader);
        glDeleteProgram(program);
        vtx_shader = pix_shader = program = 0;
        return;
    }
    delete source;

    if( !srcfile.open(pixname) ){
        error::log(error::con_only,"Erreur\n");
        error::log(error::log_warn,"Pixel shader %s: fichier introuvable, annulation...",pixname);
        glDeleteShader(pix_shader);
        glDeleteShader(vtx_shader);
        glDeleteProgram(program);
        vtx_shader = pix_shader = program = 0;
        return;
    }
    srcfile.seek(0,SEEK_END);
    filesize = srcfile.tell();
    srcfile.rewind();
    source = new char[filesize + 1];
    srcfile.read(source,1,filesize);
    source[filesize] = '\0';
    glShaderSource( pix_shader, 1, (const char**)&source, NULL);
    glCompileShader( pix_shader );
    compile_status = GL_TRUE;
    glGetShaderiv( pix_shader, GL_COMPILE_STATUS, &compile_status );
    if( compile_status != GL_TRUE ){
        error::log(error::con_only,"Erreur\n");
        error::log(error::log_warn,"Pixel shader %s: erreur lors de la compilation, annulation...",pixname);
        GLint logsize;
        glGetShaderiv(pix_shader,GL_INFO_LOG_LENGTH,&logsize);
        char* log = new char[ logsize+1 ];
        glGetShaderInfoLog(pix_shader,logsize,&logsize,log);
        error::log(error::log_warn,"%s->%s",source,log);
        delete log;
        delete source;
        glDeleteShader(pix_shader);
        glDeleteShader(vtx_shader);
        glDeleteProgram(program);
        vtx_shader = pix_shader = program = 0;
        return;
    }
    delete source;

    glAttachShader(program,vtx_shader);
    glAttachShader(program,pix_shader);
    glLinkProgram(program);
    glGetProgramiv(program,GL_LINK_STATUS,&compile_status);
    if( compile_status != GL_TRUE ){
        error::log(error::con_only,"Erreur\n");
        error::log(error::log_warn,"Shader %s | %s: erreur lors de la compilation, annulation...",vtxname,pixname);
        GLint logsize;
        glGetProgramiv(program,GL_INFO_LOG_LENGTH,&logsize);
        char* log = new char[ logsize+1 ];
        glGetProgramInfoLog(program,logsize,&logsize,log);
        error::log(error::log_warn,"%s",log);
        delete log;
        glDeleteShader(pix_shader);
        glDeleteShader(vtx_shader);
        glDeleteProgram(program);
        vtx_shader = pix_shader = program = 0;
        return;
    }
    error::log(error::con_only,"Done\n");
}
shader::~shader(){
    glDeleteShader(pix_shader);
    glDeleteShader(vtx_shader);
    glDeleteProgram(program);
}

bool shader::valid(){ return vtx_shader && pix_shader && program; }

GLuint shader::getVTX(){return vtx_shader;}
GLuint shader::getPIX(){return pix_shader;}
GLuint shader::getPRG(){return program;}

void shader::bind(shader* shr){
    if( !shr ){ glUseProgram(0); return; }
    if( !shr->valid() ){ glUseProgram(0); return; }
    glUseProgram(shr->getPRG());
}

